/*
* Copyright (C)
* Copyright (C)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "ChannelMgr.h"
#include "Player.h"
#include "World.h"

ChannelMgr::~ChannelMgr()
{
	for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr)
		delete itr->second;

	channels.clear();
}

ChannelMgr* ChannelMgr::forTeam(TeamId teamId)
{
	if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
		return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance();        // cross-faction

	if (teamId == TEAM_ALLIANCE)
		return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance();

	if (teamId == TEAM_HORDE)
		return ACE_Singleton<HordeChannelMgr, ACE_Null_Mutex>::instance();

	return NULL;
}


void ChannelMgr::LoadChannels()
{
	uint32 oldMSTime = getMSTime();
	uint32 count = 0;

	QueryResult result = CharacterDatabase.PQuery("SELECT channelId, name, team, announce, password FROM channels WHERE team = %u ORDER BY channelId ASC", _teamId);
	if (!result)
	{
		sLog->outString(">> Loaded 0 channels for %s", _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde");
		sLog->outString();
		return;
	}

	do
	{
		Field* fields = result->Fetch();
		if (!fields)
			break;

		uint32 channelDBId = fields[0].GetUInt32();
		std::string channelName = fields[1].GetString();
		std::string password = fields[4].GetString();
		std::wstring channelWName;
		Utf8toWStr(channelName, channelWName);

		Channel* newChannel = new Channel(channelName, 0, channelDBId, TeamId(fields[2].GetUInt32()), fields[3].GetUInt8());
		newChannel->SetPassword(password);
		channels[channelWName] = newChannel;

		if (QueryResult banResult = CharacterDatabase.PQuery("SELECT playerGUID, banTime FROM channels_bans WHERE channelId = %u", channelDBId))
		{
			do
			{
				Field* banFields = banResult->Fetch();
				if (!banFields)
					break;
				newChannel->AddBan(banFields[0].GetUInt32(), banFields[1].GetUInt32());
			} while (banResult->NextRow());
		}

		if (channelDBId > ChannelMgr::_channelIdMax)
			ChannelMgr::_channelIdMax = channelDBId;
		++count;
	} while (result->NextRow());

	sLog->outString(">> Loaded %u channels for %s in %ums", count, _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde", GetMSTimeDiffToNow(oldMSTime));
	sLog->outString();
}

Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId)
{
	std::wstring wname;
	Utf8toWStr(name, wname);
	wstrToLower(wname);

	ChannelMap::const_iterator i = channels.find(wname);

	if (i == channels.end())
	{
		std::string chNameLower = name;
		std::transform(chNameLower.begin(), chNameLower.end(), chNameLower.begin(), ::tolower);
		Channel* nchan = new Channel(chNameLower, channelId, 0, _teamId);
		channels[wname] = nchan;
		return nchan;
	}

	return i->second;
}

Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pkt)
{
	std::wstring wname;
	Utf8toWStr(name, wname);
	wstrToLower(wname);

	ChannelMap::const_iterator i = channels.find(wname);

	if (i == channels.end())
	{
		if (pkt)
		{
			WorldPacket data;
			MakeNotOnPacket(&data, name);
			player->GetSession()->SendPacket(&data);
		}

		return NULL;
	}

	return i->second;
}


uint32 ChannelMgr::_channelIdMax = 0;
ChannelMgr::ChannelRightsMap ChannelMgr::channels_rights;
ChannelRights ChannelMgr::channelRightsEmpty;

void ChannelMgr::LoadChannelRights()
{
	uint32 oldMSTime = getMSTime();
	channels_rights.clear();

	QueryResult result = CharacterDatabase.Query("SELECT name, flags, speakdelay, joinmessage, delaymessage, moderators FROM channels_rights");
	if (!result)
	{
		sLog->outString();
		sLog->outString(">>  Loaded 0 Channel Rights!");
		return;
	}

	uint32 count = 0;
	do
	{
		Field* fields = result->Fetch();
		std::set<uint32> moderators;
		const char* moderatorList = fields[5].GetCString();
		if (moderatorList)
		{
			Tokenizer tokens(moderatorList, ' ');
			for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i)
			{
				uint64 moderator_acc = atol(*i);
				if (moderator_acc && ((uint32)moderator_acc) == moderator_acc)
					moderators.insert((uint32)moderator_acc);
			}
		}

		SetChannelRightsFor(fields[0].GetString(), fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetString(), fields[4].GetString(), moderators);

		++count;
	} while (result->NextRow());

	sLog->outString(">> Loaded %d Channel Rights in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
	sLog->outString();
}

const ChannelRights& ChannelMgr::GetChannelRightsFor(const std::string& name)
{
	std::string nameStr = name;
	std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
	ChannelRightsMap::const_iterator itr = channels_rights.find(nameStr);
	if (itr != channels_rights.end())
		return itr->second;
	return channelRightsEmpty;
}

void ChannelMgr::SetChannelRightsFor(const std::string& name, const uint32& flags, const uint32& speakDelay, const std::string& joinmessage, const std::string& speakmessage, const std::set<uint32>& moderators)
{
	std::string nameStr = name;
	std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
	channels_rights[nameStr] = ChannelRights(flags, speakDelay, joinmessage, speakmessage, moderators);
}

void ChannelMgr::MakeNotOnPacket(WorldPacket* data, std::string const& name)
{
	data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + name.size());
	(*data) << uint8(5) << name;
}